﻿using Face8280WebSocketServer.DB.Device;
using Face8280WebSocketServer.Interface;
using Face8280WebSocketServer.Utility;
using Face8280WebSocketServer.Utility.FilterAttribute;
using Face8280WebSocketServer.Utility.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Face8280WebSocketServer.Utilities;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.WebUtilities;
using System.Net;
using Face8280WebSocketServer.DTO.FaceDevice;

namespace Face8280WebSocketServer.Controllers.FaceDevice
{
    [Route("api/[controller]")]
    [ApiController]
    public class FaceDeviceController : ControllerBase
    {

        private readonly ILogger<FaceDeviceController> _logger;
        private IFaceDriveService _DeviceDB;
        private readonly string[] _permittedExtensions = { ".pkg" };

        public FaceDeviceController(ILogger<FaceDeviceController> logger,
            IFaceDriveService door)
        {
            _logger = logger;
            _DeviceDB = door;
        }


        /// <summary>
        /// 获取已安装的设备表格
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("GetDeviceTable")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult GetDeviceTable([FromForm] DeviceQueryParameter par)
        {

            List<Expression<Func<FaceDeviceDetail, bool>>> oWheres = new();
            if (!string.IsNullOrWhiteSpace(par.DeviceID)) oWheres.Add(x => x.DeviceID.Contains(par.DeviceID));
            if (!string.IsNullOrWhiteSpace(par.DeviceName)) oWheres.Add(x => x.DeviceName.Contains(par.DeviceName));

            //_logger.LogInformation("获取设备列表");

            return new JsonResult(new JsonResultModel(_DeviceDB.GetDevicePage(oWheres, par)));
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public IActionResult AddDevice([FromForm] AddDeviceParameter parameter)
        {
            return new JsonResult( _DeviceDB.Add(new FaceDeviceDetail
            {
                DeviceID = parameter.DeviceSN,
                DeviceName = parameter.DeviceName,
                //DeviceIP = parameter.IPAddress,
                //DevicePort = parameter.Port,
                //ConnectType = parameter.ConnectType
            }));
        }

        /// <summary>
        /// 获取设备在线状态
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("GetDeviceOnlineStatus")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult GetDeviceOnlineStatus([FromForm] List<string> SNList)
        {
            return new JsonResult(new JsonResultModel(_DeviceDB.GetDeviceOnlineStatus(SNList)));
        }


        /// <summary>
        /// 更新设备信息
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("Update")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult Update([FromForm] DeviceUpdateParameter par)
        {
            FaceDeviceDetail updateModel = new FaceDeviceDetail();
            par.CopyTo(updateModel);
            JsonResultModel ret = _DeviceDB.Update(updateModel);

            return new JsonResult(ret);
        }

        /// <summary>
        /// 删除设备
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("Delete")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult Delete([FromForm] DeviceDeleteParameter par)
        {

            if (par.DeviceIDs == null)
            {
                return new JsonResult(new JsonResultModel(1, "未选择需要删除的设备"));
            }
            if (par.DeviceIDs.Count == 0)
            {
                return new JsonResult(new JsonResultModel(1, "未选择需要删除的设备"));
            }

            _DeviceDB.Delete(par.DeviceIDs);
            return new JsonResult(new JsonResultModel(par));
        }


        /// <summary>
        /// 获取设备详情
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetDeviceDetail/{id}")]
        [Authorize]
        public IActionResult GetDeviceDetail(int ID)
        {
            var device = _DeviceDB.Find<FaceDeviceDetail>(ID);
            if (device == null) return new JsonResult(new JsonResultModel(10, "设备不存在"));
            var status = _DeviceDB.GetDeviceOnlineStatus(new List<string>(new string[] { device.DeviceID }))[0];
            device.LastKeepaliveTime = status.LastKeepaliveTime;
            device.OnlineTime = status.OnlineTime;
            device.DoorStatus = status.DoorStatus;
            device.AlarmStatus = status.AlarmStatus;
            return new JsonResult(new JsonResultModel(device));
        }


        /// <summary>
        /// 获取设备出厂默认值
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("GetDefaultValue")]
        [Authorize]
        public IActionResult GetDefaultValue()
        {
            var device = _DeviceDB.GetDefaultValue();
            var sJson = JsonConvert.SerializeObject(device);

            DeviceBaseParameter par = JsonConvert.DeserializeObject<DeviceBaseParameter>(sJson);
            return new JsonResult(new JsonResultModel(par));
        }


        /// <summary>
        /// 设置设备出厂默认值
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("SetDefaultValue")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult SetDefaultValue([FromForm] DeviceBaseParameter par)
        {
            FaceDeviceDetail oModel = new FaceDeviceDetail();
            par.CopyTo(oModel);

            _DeviceDB.SaveDefaultValue(oModel);

            return new JsonResult(new JsonResultModel());
        }




        /// <summary>
        /// 设置设备出厂默认值
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost]
        [Route("UpdateDeviceSoft")]
        [Authorize]
        [TypeFilter(typeof(VerifyActionFilterAttribute))]
        public IActionResult UpdateDeviceSoft(
            [FromForm] string SoftName, [FromForm] string Ver, [FromForm] string SoftMD5, [FromForm] int DeviceID)
        {
            var oDevice = _DeviceDB.Find<FaceDeviceDetail>(DeviceID);
            if (oDevice == null)
                return new JsonResult(new JsonResultModel(1, "设备不存在"));
            string sURL = SoftName;
            if (!SoftName.StartsWith("http"))
            {
                var sPath = System.IO.Directory.GetCurrentDirectory();
                sPath = Path.Combine(sPath, "wwwroot", "softpkg");
                var sFile = Path.Combine(sPath, SoftName);
                if (!System.IO.File.Exists(sFile))
                {
                    return new JsonResult(new JsonResultModel(2, "固件文件不存在"));
                }
                var md5 = GetFileMD5(sFile);
                if (!md5.Equals(SoftMD5))
                {
                    return new JsonResult(new JsonResultModel(3, "固件文件MD5校验失败！"));
                }
                if (!SoftName.Contains(Ver))
                {
                    return new JsonResult(new JsonResultModel(4, "固件文件版本号验证失败！"));
                }
                sURL = $"/softpkg/{SoftName}";
            }

            _DeviceDB.UpdateDeviceSoft(sURL, Ver, SoftMD5, DeviceID);

            return new JsonResult(new JsonResultModel());
        }

        private static string GetFileMD5(string sFile)
        {
            MD5 md5 = MD5.Create();
            //FileStream  

            using (FileStream sRead = new FileStream(sFile, FileMode.Open, FileAccess.Read))
            {
                int iLen = (int)sRead.Length;

                //AddLog($"文件长度： {iLen}");
                byte[] md5Value = md5.ComputeHash(sRead);

                return md5Value.ToHex();
            }

        }


        /// <summary>
        /// 上传固件文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Route("UploadDeviceSoftFile")]
        public async Task<IActionResult> UploadDeviceSoftFile()
        {
            var request = HttpContext.Request;
            var ecr = System.Text.Encoding.UTF8;

            // validation of Content-Type
            // 1. first, it must be a form-data request
            // 2. a boundary should be found in the Content-Type
            if (!request.HasFormContentType ||
                !MediaTypeHeaderValue.TryParse(request.ContentType, out var mediaTypeHeader) ||
                string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
            {
                return new UnsupportedMediaTypeResult();
            }

            var reader = new MultipartReader(mediaTypeHeader.Boundary.Value, request.Body);
            var section = await reader.ReadNextSectionAsync();
            var dicValue = new Dictionary<string, string>();
            // This sample try to get the first file from request and save it
            // Make changes according to your needs in actual use
            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition,
                    out var contentDisposition);

                if (hasContentDispositionHeader && contentDisposition.DispositionType.Equals("form-data") &&
                    !string.IsNullOrEmpty(contentDisposition.FileName.Value))
                {
                    // Don't trust any file name, file extension, and file data from the request unless you trust them completely
                    // Otherwise, it is very likely to cause problems such as virus uploading, disk filling, etc
                    // In short, it is necessary to restrict and verify the upload
                    // Here, we just use the temporary folder and a random file name

                    // Get the temporary folder, and combine a random file name with it
                    var sfilename = Path.GetFileName(contentDisposition.FileName.Value);

                    if (dicValue.ContainsKey("fileName"))
                    {
                        return new JsonResult(new JsonResultModel(1, "一次仅允许上传一个文件"));
                    }
                    dicValue.Add("fileName", sfilename);
                    if (!sfilename.EndsWith(".pkg"))
                    {
                        return new JsonResult(new JsonResultModel(2, "文件后缀不正确"));
                    }
                    var _targetFilePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "softpkg", sfilename);

                    using (var targetStream = System.IO.File.Create(_targetFilePath))
                    {
                        await section.Body.CopyToAsync(targetStream);
                    }
                    var fileinfo = new FileInfo(_targetFilePath);
                    dicValue.Add("fileSize", fileinfo.Length.ToString());
                    dicValue.Add("filePath", _targetFilePath);
                    if (fileinfo.Length < 300 * 1024 * 1024)
                    {
                        System.IO.File.Delete(_targetFilePath);
                        return new JsonResult(new JsonResultModel(3, "文件太小"));
                    }
                    _logger.LogInformation($"上传大文件接口，文件名:{contentDisposition.FileName.Value}，文件大小：{fileinfo.Length}");
                    //验证文件合法性
                    string sHex;
                    using (var stm = System.IO.File.OpenRead(_targetFilePath))
                    {
                        byte[] buf = new byte[10];
                        stm.Read(buf, 0, 10);
                        sHex = buf.ToHex();

                    }
                    if (sHex != "20504B20000000001100")
                    {
                        System.IO.File.Delete(_targetFilePath);
                        return new JsonResult(new JsonResultModel(4, "文件特征不正确"));
                    }
                }
                else
                {
                    using (var targetStream = new MemoryStream())
                    {
                        await section.Body.CopyToAsync(targetStream);
                        targetStream.Position = 0;
                        byte[] buf = new byte[targetStream.Length];
                        targetStream.Read(buf, 0, (int)targetStream.Length);
                        string value = ecr.GetString(buf);
                        _logger.LogInformation($"上传大文件接口，参数名:{contentDisposition.Name.Value}，值：{value}");

                        dicValue.Add(contentDisposition.Name.Value, value);
                    }
                }

                section = await reader.ReadNextSectionAsync();
            }

            if (!dicValue.ContainsKey("fileName"))
            {
                return new JsonResult(new JsonResultModel(5, "请上传文件"));
            }

            if (!dicValue.ContainsKey("softName") || string.IsNullOrWhiteSpace(dicValue["softName"]))
            {
                return new JsonResult(new JsonResultModel(6, "固件名称不能为空"));
            }
            if (dicValue["softName"].Length < 5)
            {
                return new JsonResult(new JsonResultModel(6, "固件名称太短"));
            }


            if (!dicValue.ContainsKey("softVer") || string.IsNullOrWhiteSpace(dicValue["softVer"]))
            {
                return new JsonResult(new JsonResultModel(7, "固件版本不能为空"));
            }
            if (dicValue["softVer"].Length != 4 || !dicValue["softVer"].IsNum())
            {
                return new JsonResult(new JsonResultModel(6, "固件版本必须是4位数字"));
            }

            var settingFile = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "softpkg", "softsetting.json");
            var settingJson = System.IO.File.ReadAllText(settingFile);
            var oDetailList = JsonConvert.DeserializeObject<softDetailList>(settingJson);
            var sFileName = dicValue["fileName"];
            var hash = new Dictionary<string, softDetail>();

            foreach (var item in oDetailList.list)
            {
                hash.Add(item.url, item);
            }
            softDetail detail = null;
            if (hash.ContainsKey(sFileName))
            {
                //已存在
                detail = hash[sFileName];
            }
            else
            {
                detail = new softDetail()
                {
                    url = sFileName
                };
                hash.Add(sFileName, detail);
                oDetailList.list.Insert(0, detail);
            }
            detail.md5 = GetFileMD5(dicValue["filePath"]);
            detail.name = dicValue["softName"];
            detail.ver = dicValue["softVer"];
            settingJson = JsonConvert.SerializeObject(oDetailList);
            System.IO.File.WriteAllText(settingFile, settingJson, ecr);


            // If the code runs to this location, it means that no files have been saved
            return new JsonResult(new JsonResultModel());
        }

    }
}
